##
# Author: Samuel Abels <http://debain.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
import sys, os.path, util, gtk, pango, gobject
from urlparse                   import urlparse
from gettext                    import gettext as _
from SpiffGtkWidgets.TextEditor import TextEditor, Annotation
from SpiffGtkWidgets.color      import to_gdk


class HistoryItem(object):
    def __init__(self, workflow, adjustment):
        self.workflow_id = workflow.get_id()
        self.set_adjustment(adjustment)

    def set_adjustment(self, adjustment):
        self.scroll = adjustment.value


class WorkflowDisplay(gtk.VBox):
    def __init__(self, controller, workflow):
        gtk.VBox.__init__(self)
        self.controller = controller
        self.editor     = TextEditor()
        self.buffer     = self.editor.get_buffer()
        self.hbox       = gtk.HBox()
        self.pack_start(self.hbox, False)

        # Add the toolbar.
        toolbar = gtk.Toolbar()
        toolbar.set_style(gtk.TOOLBAR_ICONS)
        toolbar.set_icon_size(gtk.ICON_SIZE_MENU)
        self.hbox.pack_start(toolbar)

        # Add a button for the previous workflow.
        self.button_prev = gtk.ToolButton()
        self.button_prev.set_stock_id(gtk.STOCK_GO_BACK)
        self.button_prev.connect('clicked',
                                 self._button_previous_workflow_clicked_cb)
        toolbar.insert(self.button_prev, -1)

        # Add a button for the next workflow.
        self.button_next = gtk.ToolButton()
        self.button_next.set_stock_id(gtk.STOCK_GO_FORWARD)
        self.button_next.connect('clicked',
                                 self._button_next_workflow_clicked_cb)
        toolbar.insert(self.button_next, -1)

        # Add a button for the workflow history.
        self.button_history = gtk.MenuToolButton(None, None)
        toolbar.insert(self.button_history, -1)
        self.history_menu = gtk.Menu()
        self.button_history.set_menu(self.history_menu)

        # Add an edit button.
        item = gtk.ToolButton()
        item.set_stock_id(gtk.STOCK_EDIT)
        item.connect('clicked', self._button_edit_clicked_cb)
        toolbar.insert(item, -1)

        # Add the title.
        align = gtk.Alignment(.5, .5)
        align.set_padding(0, 0, 4, 0)
        self.label_title = gtk.Label()
        align.add(self.label_title)
        item = gtk.ToolItem()
        item.add(align)
        item.set_expand(True)
        toolbar.insert(item, -1)

        # Add a button for the previous version.
        self.button_prev_rev = gtk.ToolButton()
        self.button_prev_rev.set_stock_id(gtk.STOCK_GO_BACK)
        self.button_prev_rev.set_is_important(True)
        self.button_prev_rev.connect('clicked',
                                     self._button_previous_version_clicked_cb)
        toolbar.insert(self.button_prev_rev, -1)

        # Add the version number into the field.
        self.label_version = gtk.Label()
        item = gtk.ToolItem()
        item.set_is_important(True)
        item.add(self.label_version)
        toolbar.insert(item, -1)

        # Add a button for the next version.
        self.button_next_rev = gtk.ToolButton()
        self.button_next_rev.set_stock_id(gtk.STOCK_GO_FORWARD)
        self.button_next_rev.set_is_important(True)
        self.button_next_rev.connect('clicked',
                                     self._button_next_version_clicked_cb)
        toolbar.insert(self.button_next_rev, -1)

        # Add a horizontal message field/box.
        self.message_box   = gtk.EventBox()
        self.message_label = gtk.Label()
        align              = gtk.Alignment()
        align.set_padding(6, 6, 6, 6)
        self.message_box.add(align)
        align.add(self.message_label)
        self.pack_start(self.message_box, False)

        # Style the text widget.
        self.editor.set_border_window_size(gtk.TEXT_WINDOW_TOP, 6)
        self.editor.set_left_margin(6)
        self.editor.set_editable(False)
        self.editor.set_property('cursor-visible', False)
        self.editor.connect('map-event',    self._editor_map_event_cb)
        self.editor.connect('link-clicked', self._link_clicked_cb)
        self.scroll = gtk.ScrolledWindow()
        self.scroll.add(self.editor)
        self.scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        self.pack_start(self.scroll, True)

        # Style code in the text editor.
        style = {'background-full-height': True,
                 'family':                 'Monospace',
                 'left-margin':            12}
        self.code_tag = self.buffer.create_tag('code', **style)

        # Add a style for subtitles.
        self.buffer.create_tag('title',
                               scale  = pango.SCALE_LARGE,
                               weight = pango.WEIGHT_BOLD)

        self.history     = []
        self.history_pos = 0
        self.set_workflow(workflow)


    def _editor_map_event_cb(self, editor, event):
        style = self.get_style()
        self.code_tag.set_property('background-gdk',
                                   style.mid[gtk.STATE_NORMAL])
        self.editor.modify_base(gtk.STATE_NORMAL, style.bg[gtk.STATE_NORMAL])
        self.message_box.modify_bg(gtk.STATE_NORMAL, to_gdk('orange'))

        #FIXME This highly unstable hack was brough to you by a
        # guy who doesn't like to implement his own drop down menu just
        # because gtk.MenuToolButton requires two buttons in one widget.
        hbox     = self.button_history.get_children()[0]
        children = hbox.get_children()
        if len(children) == 2:
            hbox.remove(children[0])
        #FIXME end

        self.update()


    def modify_font(self, font):
        self.editor.modify_font(font)


    def update(self):
        # Update title and version number.
        name    = self.workflow.get_name()
        markup  = '<span size="14000">' + name + '</span>'
        self.label_title.set_markup(markup)

        version = _('Version %s') % self.workflow.get_version()
        self.label_version.set_text(version)

        # Update previous/next workflow button states.
        self.button_prev.set_sensitive(self.history_pos != 0)
        self.button_next.set_sensitive(self.history_pos < len(self.history) - 1)
        self.button_history.set_sensitive(len(self.history) > 1)

        # Update previous/next revision button states.
        version  = self.workflow.get_version()
        has_prev = version > 1
        has_next = self.controller.workflow_db.workflow_exists(name, version+1)
        self.button_prev_rev.set_sensitive(has_prev)
        self.button_next_rev.set_sensitive(has_next)

        # Show a history, if any.
        for child in self.history_menu.get_children():
            self.history_menu.remove(child)
        if len(self.history) != 0:
            workflow_db = self.controller.workflow_db
            end         = min(len(self.history) - 1, self.history_pos + 3)
            start       = max(0, end - 7)
            for i in range(end, start - 1, -1):
                id         = self.history[i].workflow_id
                workflow   = workflow_db.get_workflow_from_id(id)
                uri        = workflow.get_uri()
                item       = gtk.RadioMenuItem(None, workflow.get_name(), False)
                item.set_active(i == self.history_pos)
                item.connect('activate', self._history_item_clicked_cb, uri, i)
                self.history_menu.append(item)
            self.history_menu.show_all()

        # Show warnings, if any.
        if self.workflow.is_deleted():
            warning = _('Warning: You are viewing a deleted workflow.')
            markup  = '<span size="11000">' + warning + '</span>'
            self.message_label.set_markup(markup)
            self.message_box.show_all()
        elif has_next:
            warning = _('Warning: You are viewing an old version of this'
                      + ' workflow.')
            markup  = '<span size="11000">' + warning + '</span>'
            self.message_label.set_markup(markup)
            self.message_box.show_all()
        else:
            self.message_box.hide_all()

        # Insert the content.
        content = self.workflow.get_content()
        if content:
            self.buffer.restore(content)

        # Insert the annotations.
        has_annotations = len(self.buffer.get_annotations()) > 0
        self.editor.set_show_annotations(has_annotations)


    def _remember_scroll_position(self):
        if len(self.history) == 0:
            return
        item   = self.history[self.history_pos]
        adjust = self.scroll.get_vadjustment()
        item.set_adjustment(adjust)


    def set_workflow(self, workflow, add_to_history = True):
        # Add the workflow to the history, dropping future items (if any).
        if add_to_history:
            self._remember_scroll_position()
            adjustment   = self.scroll.get_vadjustment()
            self.history = self.history[:self.history_pos + 1]
            self.history.append(HistoryItem(workflow, adjustment))
            self.history_pos = len(self.history) - 1
        if len(self.history) > 30:
            self.history.pop(0)

        # Redraw.
        self.scroll.get_vadjustment().set_value(0)
        self.workflow = workflow
        self.update()


    def _button_previous_version_clicked_cb(self, *args):
        name     = self.workflow.get_name()
        version  = self.workflow.get_version() - 1
        workflow = self.controller.workflow_db.get_workflow(name, version)
        if workflow:
            self.set_workflow(workflow, False)


    def _button_next_version_clicked_cb(self, *args):
        name     = self.workflow.get_name()
        version  = self.workflow.get_version() + 1
        workflow = self.controller.workflow_db.get_workflow(name, version)
        if workflow:
            self.set_workflow(workflow, False)


    def _go_to_history(self, pos):
        self._remember_scroll_position()
        self.history_pos = pos
        item             = self.history[pos]
        db               = self.controller.workflow_db
        workflow         = db.get_workflow_from_id(item.workflow_id)
        adjustment       = self.scroll.get_vadjustment()
        self.set_workflow(workflow, False)
        gobject.idle_add(self.scroll.get_vadjustment().set_value, item.scroll)


    def _button_previous_workflow_clicked_cb(self, menu):
        self._go_to_history(self.history_pos - 1)


    def _button_next_workflow_clicked_cb(self, menu):
        self._go_to_history(self.history_pos + 1)


    def _history_item_clicked_cb(self, item, uri, pos):
        self.history_pos = pos
        self._open_link(uri, False)


    def _button_edit_clicked_cb(self, *args):
        self.emit('edit-clicked', self.workflow)


    def _open_link(self, uri, add_to_history = True):
        id         = int(urlparse(uri).path)
        current_id = self.workflow.get_id()
        workflow   = self.controller.workflow_db.get_workflow_from_id(id)
        if workflow is None:
            return
        self.set_workflow(workflow, add_to_history)


    def _link_clicked_cb(self, widget, link):
        self._open_link(link)

gobject.type_register(WorkflowDisplay)

gobject.signal_new('edit-clicked',
                   WorkflowDisplay,
                   gobject.SIGNAL_RUN_FIRST,
                   gobject.TYPE_NONE,
                   (gobject.TYPE_PYOBJECT,))
